package dungeon;


import be.kuleuven.cs.som.annotate.Basic;
/**
 * This class represents an obstacle in a dungeon that can lie between two squares.
 * An obstacle can be accessible and inherently slippery.
 * 
 * @invar	| ! (this.getFirstSquare() == null &&  this.getSecondSquare() != null)
 * @invar	| if (this.getSecondSquare() != null)
 * 			|	then this.getFirstSquare() != this.getSecondSquare()
 * @invar	| if (this.getFirstSquare() != null)
 * 			|	then this.getFirstSquare().getBorders().containsValue(this)
 * @invar	| if (this.getSecondSquare() != null)
 * 			|	then this.getSecondSquare().getBorders().containsValue(this)
 * @invar	| if (this.isAccessible() && this.getSecondSquare() != null)
 * 			|	this.getFirstSquare().getTemperature().equals(this.getSecondSquare().getTemperature())
 * 			|	&& this.getFirstSquare().getHumidityPercentage().equals(this.getSecondSquare().getHumidityPercentage())
 * 
 * @author Frederik
 *
 */
public abstract class Obstacle extends DungeonObject {
	
	/**
	 * Initialize a new obstacle with given whether this obstacle is inherently slippery or not.
	 * 
	 * @param 	inherentlySlippery
	 * 			Whether this obstacle is inherently slippery.
	 * @post	| new.isInherentlySlippery() == inherentlySlippery
	 */
	public Obstacle(Boolean inherentlySlippery){
		super(inherentlySlippery);
		}

	/**
	 * Add a square that borders this obstacle.
	 * 
	 * @param square	The square that will be added as border to this obstacle.
	 * @post	| if(this.getFirstSquare() == null)
	 * 			|	then new.getFirstSquare() == square
	 * @post	| if (this.getFirstSquare() != null && this.getSecondSquare() == null 
	 * 			|		&& this.isValidSquareToAdd(square))
	 * 			|	then new.getSecondSquare() == square
	 */
	void addSquare(Square square){	// package private
		if (!isValidSquareToAdd(square))
			throw new IllegalArgumentException();
		if (getSecondSquare() != null)
			throw new IllegalStateException("This obstacle already lies inbetween 2 squares.");
		if (getFirstSquare() == null)
			setFirstSquare(square);
		else 
			setSecondSquare(square);
		}
	
	/**
	 * Check whether it is a valid square to add to this obstacle.
	 * 
	 * @param 	square
	 * 			The square that is or isn't valid to border this obstacle.
	 * @return	| if (square != null && square != this.getFirstSquare())
	 * 			|	then result == true
	 */
	public boolean isValidSquareToAdd(Square square) {
		if(square == null)
			return false;
		if(getFirstSquare() == square)
			return false;
		return true;
	}

	/**
	 * Delete a square that borders this obstacle.
	 * 
	 * @param square	The square that will be deleted.
	 * @post	| if(this.containsSquare(square) && this.getSecondSquare() == null)
	 * 			| 	then new.getFirstSquare() == null
	 * @post	|  if(this.containsSquare(square) && this.getSecondSquare() != null)
	 * 			| 	then ! new.containsSquare(square)
	 * 			|		&& new.getSecondSquare() == null
	 * 			|		&& new.getFirstSquare() != null
	 */
	void deleteSquare(Square square){	// package private
		if(!containsSquare(square))
			throw new IllegalArgumentException("This obstacle does not contain the given square.");
		if(getSecondSquare() == square)
			setSecondSquare(null);
		if(getFirstSquare() == square){
			if(getSecondSquare() == null)
				setFirstSquare(null);
			else {
				setFirstSquare(getSecondSquare());
				setSecondSquare(null);
			}
		}
	}

	/**
	 * Return the first square that borders this obstacle.
	 * 
	 * @return	The first square that borders this obstacle.
	 */
	@Basic
	public Square getFirstSquare(){
		return firstSquare;
	}

	/**
	 * Set the first square that borders this obstacle.
	 * 
	 * @param square	The first square that borders this obstacle.
	 */
	private void setFirstSquare(Square square){
		this.firstSquare = square;
	}

	/**
	 * The first square that borders this obstacle.
	 */
	private Square firstSquare;

	/**
	 * Return the second square that borders this obstacle.
	 * 
	 * @return	The second square that borders this obstacle.
	 */
	@Basic
	public Square getSecondSquare(){
		return secondSquare;
	}

	/**
	 * Set the second square that borders this obstacle.
	 * 
	 * @param square	The second square that borders this obstacle.
	 */
	private void setSecondSquare(Square square){
		this.secondSquare = square;
	}

	/**
	 * The second square that borders this obstacle.
	 */
	private Square secondSquare;

	/**
	 * Returns whether this obstacle borders the given square.
	 * 
	 * @param square	The square that does or doesn't border this obstacle.
	 * @return			Whether this obstacle borders the given square.
	 */
	public boolean containsSquare(Square square){
		if(square == null)
			return false;
		return(this.getFirstSquare() == square || this.getSecondSquare() == square);
	}

	/**
	 * Return the other square that borders this obstacle besides the given square.
	 * 
	 * @param square	The square that borders this obstacle.
	 * @return			The other square that borders this obstacle.
	 * @throws IllegalArgumentException
	 * 					!this.containsSquare(square)
	 */
	public Square getOtherSquare(Square square) throws IllegalArgumentException{
		if(!this.containsSquare(square))
			throw new IllegalArgumentException();
		if (this.getFirstSquare() == square)
			return this.getSecondSquare();
		return this.getFirstSquare();
	}
	
	/**
	 * Check whether a person can walk through this obstacle (e.g. an open door).
	 * If this isn't the case, it also means that the two squares are completely isolated concerning temperature and humidity.
	 * 
	 * @return	Whether this obstacle is accessible.
	 */
	public abstract boolean isAccessible();
}
